﻿using System;
using System.Collections.ObjectModel;
using SCG = System.Collections.Generic;

using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using GASS.OpenCL;
using GASS.OpenCL.OpenCLDriver;

namespace Nemerle.OpenCL {

    /// <summary>
    /// Description of Device.
    /// </summary>
    [InfoProperty(CLDeviceInfo.AddressBits, uint)]
    [InfoProperty(CLDeviceInfo.Available, bool, Flags(no_cache))]
    [InfoProperty(CLDeviceInfo.CompilerAvailable, bool)]
    
    [InfoProperty(CLDeviceInfo.EndianLittle, bool)]
    [InfoProperty(CLDeviceInfo.ErrorCorrectionSupport, bool)]
    [InfoProperty(CLDeviceInfo.ExecutionCapabilities, CLDeviceExecCapabilities)]

    [InfoProperty(CLDeviceInfo.GlobalMemCacheSize, ulong)]
    [InfoProperty(CLDeviceInfo.GlobalMemCacheType, CLDeviceMemCacheType)]
    [InfoProperty(CLDeviceInfo.GlobalMemCacheLineSize, uint)]
    [InfoProperty(CLDeviceInfo.GlobalMemSize, ulong)]

    [InfoProperty(CLDeviceInfo.LocalMemSize, ulong)]
    [InfoProperty(CLDeviceInfo.LocalMemType, CLDeviceLocalMemType)]

    [InfoProperty(CLDeviceInfo.ImageSupport, bool)]
    [InfoProperty(CLDeviceInfo.Image2DMaxHeight, long, Flags(size_t))]
    [InfoProperty(CLDeviceInfo.Image2DMaxWidth, long, Flags(size_t))]
    [InfoProperty(CLDeviceInfo.Image3DMaxDepth, long, Flags(size_t))]
    [InfoProperty(CLDeviceInfo.Image3DMaxHeight, long, Flags(size_t))]
    [InfoProperty(CLDeviceInfo.Image3DMaxWidth, long, Flags(size_t))]

    [InfoProperty(CLDeviceInfo.MaxReadImageArgs, uint)]
    [InfoProperty(CLDeviceInfo.MaxWriteImageArgs, uint)]

    [InfoProperty(CLDeviceInfo.MaxClockFrequency, uint)]
    [InfoProperty(CLDeviceInfo.MaxComputeUnits, uint)]
    [InfoProperty(CLDeviceInfo.MaxConstantArgs, uint)]
    [InfoProperty(CLDeviceInfo.MaxConstantBufferSize, ulong)]
    [InfoProperty(CLDeviceInfo.MaxMemAllocSize, ulong)]
    [InfoProperty(CLDeviceInfo.MaxParameterSize, long, Flags(size_t))]
    [InfoProperty(CLDeviceInfo.MaxSamplers, uint)]

    [InfoProperty(CLDeviceInfo.MaxWorkGroupSize, long, Flags(size_t))]
    [InfoProperty(CLDeviceInfo.MaxWorkItemDimensions, uint)]
    [InfoProperty(CLDeviceInfo.MaxWorkItemSizes, NDRange)]

    [InfoProperty(CLDeviceInfo.MemBaseAddrAlign, uint)]
    [InfoProperty(CLDeviceInfo.MinDataTypeAlignSize, uint)]

    [InfoProperty(CLDeviceInfo.PreferredVectorWidthChar, uint)]
    [InfoProperty(CLDeviceInfo.PreferredVectorWidthShort, uint)]
    [InfoProperty(CLDeviceInfo.PreferredVectorWidthInt, uint)]
    [InfoProperty(CLDeviceInfo.PreferredVectorWidthLong, uint)]
    [InfoProperty(CLDeviceInfo.PreferredVectorWidthFloat, uint)]
    [InfoProperty(CLDeviceInfo.PreferredVectorWidthDouble, uint)]

    [InfoProperty(CLDeviceInfo.ProfilingTimerResolution, long, Flags(size_t))]
    [InfoProperty(CLDeviceInfo.QueueProperties, CLCommandQueueProperties)]

    //[InfoProperty(CLDeviceInfo.HalfFPConfig, CLDeviceFPConfig)]
    [InfoProperty(CLDeviceInfo.SingleFPConfig, CLDeviceFPConfig)]
    //[InfoProperty(CLDeviceInfo.DoubleFPConfig, CLDeviceFPConfig)]

    [InfoProperty(CLDeviceInfo.Name, string)]
    [InfoProperty(CLDeviceInfo.Vendor, string)]
    [InfoProperty(CLDeviceInfo.VendorID, uint)]
    [InfoProperty(CLDeviceInfo.Type, CLDeviceType)]
    [InfoProperty(CLDeviceInfo.Profile, string)]
    [InfoProperty(CLDeviceInfo.Version, string)]
    [InfoProperty(CLDeviceInfo.Extensions, ReadOnlyCollection.[string])]
    [EqualityByIdPattern]
    public sealed class Device {

        public static DefaultDevice : Device {
            defaultDevice : LazyValue[Device] = lazy (
                match(Platform.DefaultPlatform) {
                    | null => null
                    | platform => platform.GetDevice(CLDeviceType.All)
                }
            );
            get { defaultDevice.Value }
        }

        public static DefaultAcceleratedDevice : Device {
            acceleratedDevice : LazyValue[Device] = lazy(
                match(Platform.DefaultPlatform) {
                    | null => null
                    | platform => platform.GetDevice(CLDeviceType.Accelerator %| CLDeviceType.GPU)
                }
            );
            get { acceleratedDevice.Value }
        }

        internal this(platform : Platform, id : CLDeviceID) {
            this.platform = platform;
            this.id = id;
        }

        [Accessor] platform : Platform;

        [Accessor] id : CLDeviceID;

        public override ToString() : string {
            Name
        }

    }

}
